#pragma once

#include <iostream>
#include <string>
#include <memory>
#include <cerrno>
#include <functional>
#include <unordered_map>

#include "Log.hpp"
#include "nocopy.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"
#include "Common.hpp"

class Connection;
class TcpServer;

uint32_t EVENT_IN = (EPOLLIN | EPOLLET); // 设置ET模式
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);
const static int g_buffer_size = 128;


// std::function 是一个模板类；func_t，它是一个 std::function 的特化，存储一个函数指针对象
using func_t = std::function<void(std::shared_ptr<Connection>)>;
using except_func = std::function<void(std::weak_ptr<Connection>)>;

class Connection
{
public:
    Connection(int sock)
    :_sock(sock)
    {
    }
    void SetHandler(func_t recv_cb, func_t send_cb, except_func except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }
    int SockFd() { return _sock; }
    void AppendInBuffer(const std::string &info)
    {
        _inbuffer += info;
    }
    void AppendOutBuffer(const std::string &info)
    {
        _outbuffer += info;
    }
    std::string &Inbuffer() // for debug
    {
        return _inbuffer;
    }
    std::string &OutBuffer()
    {
        return _outbuffer;
    }
    void SetWeakPtr(std::weak_ptr<TcpServer> tcp_server_ptr)
    {
        _tcp_server_ptr = tcp_server_ptr;
    }
    ~Connection()
    {
    }

private:
    int _sock;
    // string 充当缓冲区，但是string无法处理二进制流，更好使用vector，但是vector需要单独写缓冲区，比较麻烦，所以这里采用string
    std::string _inbuffer;
    std::string _outbuffer;

public: //直接放为公有了，正常还要写一些get获取私有
    func_t _recv_cb; // 读回调，读到的数据放到inbuffer中
    func_t _send_cb;
    except_func _except_cb;

    // 添加一个回指指针
    std::weak_ptr<TcpServer> _tcp_server_ptr; // 这里weak_ptr更好，因为并不管理tcp_server_ptr的生命周期，只是引用一下 但是有bug??
    //std::shared_ptr<TcpServer> _tcp_server_ptr;

    std::string _ip;
    uint16_t _port;
};

// enable_shared_from_this:可以提供返回当前对象的this对应的shared_ptr
class TcpServer :public std::enable_shared_from_this<TcpServer>,public nocopy
{
    static const int num = 64;

public:
    TcpServer(uint16_t port,func_t OnMessage)
        : _port(port)
        , _quit(true)
        ,_OnMessage(OnMessage)
        , _epoller_ptr(new Epoller())
        , _listensock_ptr(new Sock())
    {
    }
    void Init()
    {
        _listensock_ptr->Socket();
        SetNonBlockOrDie(_listensock_ptr->Fd());
        _listensock_ptr->Bind(_port);
        _listensock_ptr->Listen();
        lg(Info, "create listen socket success: %d\n", _listensock_ptr->Fd());
        AddConnection(_listensock_ptr->Fd(),
                      EVENT_IN, std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr); // 将Accepter绑给了fd对应的recv_cb，执行读回调就是执行Accepter,_1表示这是绑定的第一个方法
    }
    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, except_func except_cb,
                       const std::string &ip = "0.0.0.0", uint16_t port = 0)
    {
        // 给对应listensock也要建立一个Connection对象，将listensock添加到Connection中
        //std::shared_ptr<Connection> new_connection = std::make_shared<Connection>(sock, std::shared_ptr<TcpServer>(this)); // make_shared直接构造一个智能指针，并为其分配并初始化所管理对象，比直接shared_ptr构造更高效,能减少内存分配次数
        
        //上面是错误版本
        // std::shared_ptr<Connection> new_connection = std::make_shared<Connection>(sock);
        std::shared_ptr<Connection> new_connection(new Connection(sock));
        new_connection->SetWeakPtr(shared_from_this()); // shared_from_this(): 返回当前对象的shared_ptr
        new_connection->SetHandler(recv_cb, send_cb, except_cb);
        new_connection->_ip = ip;
        new_connection->_port = port;

        // 同时，listensock和Connection放入_connections
        // 然后对应fd一旦就绪，然后根据fd在_connections中查表，查到对应的Connection，然后根据Connection的读回调将数据读到inbuffer中
        // 所以每个fd都有自己的回调,这样每个fd都能读到自己的inbuffer中了
        _connections.insert(std::make_pair(sock, new_connection));

        // 我们添加对应的事件，加入到内核中，即添加fd对应的事件event
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_ADD, sock, event);

        lg(Debug, "add a new connection success, sockfd is : %d", sock);
    }

    // 链接管理器
    void Accepter(std::weak_ptr<Connection> conn)
    {
        auto connection = conn.lock();
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = ::accept(connection->SockFd(), (struct sockaddr *)&peer, &len); //::accept代表用的是原生的系统调用
            if (sock > 0)
            {
                uint16_t peerport = ntohs(peer.sin_port);
                char ipbuf[128];
                inet_ntop(AF_INET, &peer.sin_addr.s_addr, ipbuf, sizeof(ipbuf));
                lg(Debug, "get a new client, get info-> [%s:%d], sockfd : %d", ipbuf, peerport, sock);

                SetNonBlockOrDie(sock);
                //listensock只需要设置_recv_cb，而其他的sock，读 写 异常都要处理
                AddConnection(sock, EVENT_IN,
                              std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                              std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                              std::bind(&TcpServer::Excepter, this, std::placeholders::_1),
                              ipbuf, peerport); 
            }
            else
            {
                if (errno == EWOULDBLOCK) // 没了还在读
                    break;
                else if (errno == EINTR) // 这个错误码通常表示一个系统调用由于接收到信号而被中断
                    continue;
                else // 读出错了
                    break;
            }
        }
    }

    //3个事件管理器
    //应不应该关心数据的格式？？？不应该！！服务器只要IO数据就可以，有没有读完，报文的格式细节，你不用管。
    void Recver(std::weak_ptr<Connection> conn)
    {
        if(conn.expired()) return;
        auto connection = conn.lock();
        //std::cout<<"haha got you!!!,sockfd: "<<connection->SockFd()<<std::endl;
        int sock = connection->SockFd();
        while (true)
        {
            char buffer[g_buffer_size];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0); //已经设为非阻塞了，当前为非阻塞读取
            if (n > 0)
            {
                connection->AppendInBuffer(buffer);
            }
            else if (n == 0)
            {
                lg(Info, "sockfd: %d, client info %s:%d quit...", sock, connection->_ip.c_str(), connection->_port);
                connection->_except_cb(connection);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK)
                    break;
                else if (errno == EINTR)
                    continue;
                else
                {
                    lg(Warning, "sockfd: %d, client info %s:%d excepter hander", sock, connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        //将读到的数据交给上层，数据有了，但是不一定全，所以要求上层： 1. 检测 2. 如果有完整报文，就处理。 所以再往上层就是对数据做是否是一个完整的报文以及序列化等处理了
        _OnMessage(connection); // 你读到的sock所有的数据都在connection里
    }
    void Sender(std::weak_ptr<Connection> conn)
    {
        if(conn.expired()) return;
        auto connection = conn.lock();
        auto &outbuffer = connection->OutBuffer();
        while(true)
        {
            ssize_t n = send(connection->SockFd(), outbuffer.c_str(), outbuffer.size(), 0);
            if(n > 0)
            {
                outbuffer.erase(0, n);
                if(outbuffer.empty()) break;
            }
            else if(n == 0)
            {
                return;
            }
            else
            {
                if(errno == EWOULDBLOCK) break;
                else if(errno == EINTR) continue;
                else{
                    lg(Warning, "sockfd: %d, client info %s:%d send error...", connection->SockFd(), connection->_ip.c_str(), connection->_port);
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        if(!outbuffer.empty()) //没发完,就开启对写事件的关心，一旦发送缓冲区有空间了，底层自动做事件的派发，然后执行写回调继续发
        {
            // 开启对写事件的关心
            EnableEvent(connection->SockFd(), true, true);
        }
        else  //发完了
        {
            // 关闭对写事件的关心,发完了就关掉写事件的关心了
            EnableEvent(connection->SockFd(), true, false);
        }
    }

    //读写异常都由这个管理器处理
    void Excepter(std::weak_ptr<Connection> conn)
    {
        if(conn.expired()) return;
        auto connection=conn.lock();
        lg(Warning, "Excepter hander sockfd: %d, client info %s:%d recv error...", connection->SockFd(), connection->_ip.c_str(), connection->_port);
        // 1. 移除对特定fd的关心
        // EnableEvent(connection->SockFd(), false, false);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_DEL, connection->SockFd(), 0);
        // 2. 关闭异常的文件描述符
        lg(Debug, "close %d done...\n", connection->SockFd());
        close(connection->SockFd());
        // 3. 从unordered_map中移除
        lg(Debug, "remove %d from _connections...\n", connection->SockFd());
        // TODO bug
        // auto iter = _connections.find(fd);
        // if(iter == _connections.end()) return;
        // _connections.erase(iter);
        // _connections[fd].reset();
        _connections.erase(connection->SockFd());
    }
    
    void EnableEvent(int sock, bool readable, bool writeable)
    {
        uint32_t events = 0;
        events |= ((readable ? EPOLLIN : 0) | (writeable ? EPOLLOUT : 0) | EPOLLET);
        _epoller_ptr->EpollerUpdate(EPOLL_CTL_MOD, sock, events);
    }

    // 判断fd是否存在connections表中
    bool IsConnectionsSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }

    void Dispatcher(int timeout)
    {
        int n = _epoller_ptr->EpollerWait(revs, num, timeout);
        for (int i = 0; i < n; i++)
        {
            uint32_t events = revs[i].events;
            int sock = revs[i].data.fd;

            // 统一把事件异常转换为读写问题，这样只需要在读写的时候判段异常即可，异常处理就不会扩散到其它的代码了
            if (events & EPOLLERR)
                events |= (EPOLLIN | EPOLLOUT); // 如果事件出异常就转换为读写
            if (events & EPOLLHUP)
                events |= (EPOLLIN | EPOLLOUT); // 如果事件出异常就转换为读写
            // 只需要处理EPOLLIN EPOLLOUT
            if ((events & EPOLLIN) && IsConnectionsSafe(sock))
            {
                if (_connections[sock]->_recv_cb) // 如果这个回调被设置过了，就执行 ???这是怎么当if条件的？？？
                    _connections[sock]->_recv_cb(_connections[sock]);
            }
            if ((events & EPOLLOUT) && IsConnectionsSafe(sock))
            {
                if (_connections[sock]->_send_cb) // 如果这个回调被设置过了，就执行
                    _connections[sock]->_send_cb(_connections[sock]);
            }
        }
    }

    void Loop()
    {
        _quit = false;
        // AddConnection();
        while (!_quit)
        {
            Dispatcher(3000);
            PrintConnection();
        }
        _quit = true;
    }
    void PrintConnection()
    {
        std::cout << "_connections fd list: ";
        for (auto &connection : _connections)
        {
            std::cout << connection.second->SockFd() << ", ";
            std::cout << "inbuffer: " << connection.second->Inbuffer().c_str();
        }
        std::cout << std::endl;
    }
    ~TcpServer()
    {
    }

private:
    std::shared_ptr<Epoller> _epoller_ptr;
    std::shared_ptr<Sock> _listensock_ptr;
    std::unordered_map<int, std::shared_ptr<Connection>> _connections; // fd到连接的映射
    struct epoll_event revs[num];
    uint16_t _port;
    bool _quit;

    // 让上层处理信息
    func_t _OnMessage;
};